package com.lingjtx.common.other.http;

import com.alibaba.fastjson2.JSON;
import com.lingjtx.common.core.handle.ObjectPool;
import com.lingjtx.common.other.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.nio.charset.StandardCharsets;
import java.util.StringJoiner;

/**
 * 为了不引入第三方sdk，减少依赖，实现一个简单的http请求库
 * 支持同步异步请求
 *
 * @author john liu
 */
public class Network {

    public static <T> T remote(final Class<T> service, ObjectPool<HttpClient> objectPool) {
        if (!service.isInterface()) {
            throw new IllegalArgumentException("The provided class must be an interface: " + service);
        }
        Object proxy = Proxy.newProxyInstance(
                service.getClassLoader(),
                new Class[]{service},
                new RemoteServiceInvocationHandler(objectPool)
        );
        return service.cast(proxy);
    }

    private record RemoteServiceInvocationHandler(ObjectPool<HttpClient> objectPool) implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 处理 Object 的基础方法
            if (method.getDeclaringClass() == Object.class) {
                return handleObjectMethod(proxy, method, args);
            }

            GET get = method.getAnnotation(GET.class);
            POST post = method.getAnnotation(POST.class);

            if (get != null) {
                return handleGetRequest(get, method, args);
            } else if (post != null) {
                return handlePostRequest(post, method, args);
            } else {
                throw new IllegalStateException("方法 " + method.getName() + " 缺少 @GET 或 @POST 注解");
            }
        }

        private Object handleObjectMethod(Object proxy, Method method, Object[] args) {
            return switch (method.getName()) {
                case "toString" -> "RemoteService Proxy, handler=" + this;
                case "hashCode" -> System.identityHashCode(proxy);
                case "equals" -> proxy == args[0];
                default -> throw new UnsupportedOperationException(method.getName() + " is not supported");
            };
        }

        private Object handleGetRequest(GET get, Method method, Object[] args) {
            String url = buildUrl(get.value(), method, args, true);
            HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create(url)).GET();
            return buildHttpCall(method, builder, args);
        }

        private Object handlePostRequest(POST post, Method method, Object[] args) {
            String url = buildUrl(post.value(), method, args, false);
            Object body = findAnnotatedParam(method.getParameters(), args, Body.class);
            String json = body != null ? JSON.toJSONString(body) : "";
            HttpRequest.Builder builder = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .POST(HttpRequest.BodyPublishers.ofString(json));
            return buildHttpCall(method, builder, args);
        }

        private Object buildHttpCall(Method method, HttpRequest.Builder builder, Object[] args) {
            // headers
            addHeaders(builder, method, method.getParameters(), args);

            HttpRequest request = builder.build();
            ResponseFormat format = method.getAnnotation(ResponseFormat.class);
            ResponseType responseType = format != null ? format.value() : ResponseType.JSON;

            Type returnType = method.getGenericReturnType();
            if (returnType instanceof ParameterizedType pt) {
                Type actualType = pt.getActualTypeArguments()[0];
                return new HttpCall<>(request, objectPool, responseType, actualType);
            }

            throw new IllegalStateException("方法返回类型不是泛型: " + returnType);
        }

        private String buildUrl(String uri, Method method, Object[] args, boolean isGet) {
            Parameter[] parameters = method.getParameters();
            StringBuilder query = new StringBuilder();
            String url = uri;

            // 替换路径参数
            for (int i = 0; i < parameters.length; i++) {
                if (parameters[i].isAnnotationPresent(Path.class)) {
                    Path path = parameters[i].getAnnotation(Path.class);
                    url = url.replace("{" + path.value() + "}", URLEncoder.encode(args[i].toString(), StandardCharsets.UTF_8));
                }
            }

            // 拼接 @Query 参数
            for (int i = 0; i < parameters.length; i++) {
                if (parameters[i].isAnnotationPresent(Query.class)) {
                    Query q = parameters[i].getAnnotation(Query.class);
                    query.append(!query.isEmpty() ? "&" : "")
                            .append(q.value()).append("=")
                            .append(URLEncoder.encode(args[i].toString(), StandardCharsets.UTF_8));
                }
                if (parameters[i].isAnnotationPresent(BaseUrl.class)) {
                    url = args[i] + uri;
                }
            }

            // 拼接 @QueryObject 参数
            if (isGet) {
                Object queryObj = findAnnotatedParam(parameters, args, QueryObject.class);
                if (queryObj != null) {
                    String queryStr = beanToString(queryObj);
                    if (!queryStr.isEmpty()) {
                        query.append(!query.isEmpty() ? "&" : "").append(queryStr);
                    }
                }
            }

            return !query.isEmpty() ? url + "?" + query : url;
        }

        private void addHeaders(HttpRequest.Builder builder, Method method, Parameter[] parameters, Object[] args) {
            // 动态 Header
            for (int i = 0; i < parameters.length; i++) {
                if (parameters[i].isAnnotationPresent(Header.class)) {
                    String key = parameters[i].getAnnotation(Header.class).value();
                    builder.header(key, args[i].toString());
                }
            }

            // 静态 Headers 注解
            for (Headers h : method.getAnnotationsByType(Headers.class)) {
                for (String entry : h.value()) {
                    String[] kv = entry.split(":");
                    if (kv.length == 2) {
                        builder.header(kv[0].trim(), kv[1].trim());
                    }
                }
            }
        }

        private Object findAnnotatedParam(Parameter[] parameters, Object[] args, Class<? extends Annotation> anno) {
            for (int i = 0; i < parameters.length; i++) {
                if (parameters[i].isAnnotationPresent(anno)) {
                    return args[i];
                }
            }
            return null;
        }
    }

    private static String beanToString(Object bean) {
        if (bean == null) return "";
        StringJoiner joiner = new StringJoiner("&");
        for (Field field : bean.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                Object value = field.get(bean);
                if (value != null) {
                    joiner.add(field.getName() + "=" +
                            URLEncoder.encode(value.toString(), StandardCharsets.UTF_8));
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("字段读取失败: " + field.getName(), e);
            }
        }
        return joiner.toString();
    }
}